package com.yutian.springbootwebflux.controller;


import com.yutian.springbootwebflux.base.BaseResult;
import com.yutian.springbootwebflux.exception.CustomException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

@Api(tags="上传资源")
@CrossOrigin
@RestController
@RequestMapping("platform/upload/api/")
public class UploadController {
    private static final Logger log = LoggerFactory.getLogger(UploadController.class);

    // 定义在某个控制类内,所定义的异常处理控制器方法仅仅覆盖当前控制器类内各个方法所发生的这类异常。但推荐定义全局异常处理类
    /*@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
    @ExceptionHandler(ResourceNotFoundException.class)
    public void notFound() {
    }*/


    @Value("${upload.common.fdsPrefix:test/}")
    private String uploadFdsPre;

   

    /**
     * 上传文件，返回下载地址
     * 图片、视频、礼包兑换码文件上传操作
     *
     * @param file
     * @return
     */
    @ApiOperation("上传文件")
    @PostMapping("common/res")
    public Mono<BaseResult> uploadFile(@RequestPart("file") FilePart file, CommonUploadFileReq req) throws Exception {
        String objectName = newObjectName(file, req);

        return uploadFile(file, objectName);
    }


    public Mono<BaseResult> uploadFile(@RequestPart("file") FilePart file, String objectName) {
        String url = null;
        File tempFile = null;
        try {
            tempFile = createTempFile(file);
            log.info("file.save.suc: {}", file.filename());
        } catch (Exception e) {
            log.error("file.save.failed: {}", file.filename(), e);
            return Mono.error(new CustomException("file.save.failed"));
        }

        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(tempFile);
            // url = FdsUtil.upload(fdsParam, inputStream, objectName);
        } catch (Exception e) {
            log.error("file.upload.failed", e);
            return Mono.error(new CustomException("file.upload.failed"));
        } finally {
            IOUtils.closeQuietly(inputStream);
            try {
                tempFile.delete();
            } catch (Exception e) {
            }
        }
        BaseResult response = BaseResult.success(url);
        return Mono.just(response);
    }

    /**
     * 生成临时文件。
     * 若文件较小，可以filePart.transferTo(tempFile.toFile());
     * @param file
     * @return
     * @throws IOException
     */
    private File createTempFile(FilePart file) throws IOException {
        // 如果没有CountDownLatch，则尚未doOnComplete就已先返回，导致文件还未完全生成
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Path tempFile = Files.createTempFile("upload", file.filename());
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE);
        // http://opoo.org/2020/spring-webflux-body-databuffer/
        DataBufferUtils.write(file.content(), channel, 0)
                .doOnComplete(() -> {
                    log.info("tempFile.write.doOnComplete:{}", file.filename());
                }).doFinally((result) -> {
            // result is class reactor.core.publisher.SignalType
            log.info("tempFile.write.doFinally:{}, result:{}", file.filename(), result);
            countDownLatch.countDown();
            try {
                channel.close();
            } catch (IOException e) {
            }
        }).subscribe();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
        }
        return tempFile.toFile();
    }


    private String newObjectName(FilePart file, CommonUploadFileReq req) {
        String fileName = StringUtils.isNotBlank(req.getFileName()) ? req.getFileName() : file.filename();
        int dotIndex = fileName.lastIndexOf('.');
        String extension = (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);

        String fdsFileName = UUID.randomUUID().toString().replace("-", "");
        if (StringUtils.isNotBlank(extension)) {
            fdsFileName = fdsFileName + "." + extension.toLowerCase();
        }
        return uploadFdsPre + req.getObjectDir() + "/" + DateFormatUtils.format(new Date(), "yyyyMM") + "/" + fdsFileName;
    }
}